/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 compound_object_model.cpp

	$Header: /heroes4/compound_object_model.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#include "compound_object_model.h"

#include "battlefield_metrics.h"
#include "combat_object_type.h"
#include "combat_reader.h"
#include "rational.h"

// --------------------------------------------------------------------------
// t_combat_object_model class
// --------------------------------------------------------------------------
t_object_segment::t_object_segment( t_combat_object_model_root const&	object_data,
									int									footprint_size,
									t_map_point_2d const&				offset,
									t_image_sequence_base_base const&	frame_order,
									t_bitmap_group const&				bitmaps )
				: t_image_sequence( frame_order, bitmaps ),
				  t_combat_object_model_root( object_data )
{
	m_footprint_size = footprint_size;
	m_segment_offset = offset;
}

// --------------------------------------------------------------------------
// copy bitmaps according to a mask
// --------------------------------------------------------------------------
static void copy_bitmaps( t_bitmap_group const& source, t_bitmap_group& dest,
						  t_screen_point const& origin, t_bitmap_line_array const& shape )
{
	int                   i;
	t_bitmap_layer const* layer;
	t_bitmap_layer*       result;

	for (i = 0; i < source.size(); i++)
	{
		layer = &source[i];
		result = layer->copy( origin, shape );
		dest.push_back( result );
	}
}

// --------------------------------------------------------------------------
// get base point
// --------------------------------------------------------------------------
t_screen_point get_base_point( t_map_point_3d const& cell_offset, double scale )
{
	t_screen_point base_point;

	base_point.x = k_battlefield_cell_width / 2 * cell_offset.column * scale
				 - k_battlefield_cell_width / 2 * cell_offset.row * scale;
	base_point.y = k_battlefield_cell_height / 2 * cell_offset.column * scale
				 + k_battlefield_cell_height / 2 * cell_offset.row * scale;
	base_point.y -= cell_offset.height * scale;
	return base_point;
}


// --------------------------------------------------------------------------
// t_combat_object_model_base class
// --------------------------------------------------------------------------
t_compound_object_model::t_compound_object_model( t_combat_object_model_24 const& source,
												  double scale )
{
	t_bitmap_group bitmaps;

	m_type = source.get_type();
	if (scale == 1.0)
	{
		bitmaps = t_bitmap_group( source.get_frames() );
	}
	else
	{
		t_bitmap_group_24 frames = source.get_frames();

		frames.offset( source.get_offset() );
		scale_group( frames, bitmaps, scale );
		bitmaps.offset( -source.get_offset() );
	}

	int segment_size;

	m_footprint_size = source.get_footprint_size();
	// determine size of subchunks.  1 for passable objects, as large as possible for
	// others
	if (source.blocks_movement() || source.is_underlay() 
		|| source.get_type() == k_obstacle_special || source.get_type() == k_obstacle_castle_tower)
		segment_size = greatest_common_divisor( m_footprint_size.row,
											    m_footprint_size.column );
	else
		segment_size = 1;

	// handle case where this isn't really compound
	if ((segment_size == m_footprint_size.row && segment_size == m_footprint_size.column)
		|| m_footprint_size.row == 0 || m_footprint_size.column == 0 )
	{
		t_object_segment* segment;

		segment = new t_object_segment( source, segment_size, t_map_point_2d(0,0), source,
			                            bitmaps );
		m_segments.push_back( segment );
		return;
	}

	t_map_point_3d		cell_offset(0,0,0);
	t_screen_rect		image_rect = bitmaps.get_rect();
	t_screen_point      shape_origin;
	t_screen_point		base_point;
	t_screen_point		left_tile_base;
	t_screen_point		right_tile_base;
	t_screen_point      bottom_tile_base;
	t_bitmap_line		line;
	t_bitmap_line_array lines;
	int					i;
	int					center;
	int					width;
	int					image_width = image_rect.width();
	int					half_segment_height;
	int					segment_height;
	t_map_point_2d      left_offset( segment_size, 0 );
	t_map_point_2d      right_offset( 0, segment_size );
	t_map_point_2d      down_offset( segment_size, segment_size );

	// chop image up into segment_size x segment_size chunks
	cell_offset.height = source.get_height();
	for (cell_offset.row = 0; cell_offset.row < m_footprint_size.row;
	     cell_offset.row += segment_size)
	{
		for (cell_offset.column = 0; cell_offset.column < m_footprint_size.column;
		     cell_offset.column += segment_size)
		{
			// find screen coordinate of top of tile
			base_point = get_base_point( cell_offset, scale ) - source.get_offset();
			left_tile_base = get_base_point( cell_offset + left_offset, scale )
				             - source.get_offset();
			right_tile_base = get_base_point( cell_offset + right_offset, scale )
				              - source.get_offset();
			bottom_tile_base = get_base_point( cell_offset + down_offset, scale )
				               - source.get_offset();
			lines.clear();
			shape_origin.x = image_rect.left;
			center = base_point.x - shape_origin.x;
			left_tile_base.x -= image_rect.left;
			right_tile_base.x -= image_rect.left;
			if (cell_offset.row == 0 && cell_offset.column == 0)
			{   // for the topmost tile only, include all the lines above half the height
				shape_origin.y = image_rect.top;
				line.left = 0;
				line.right = image_width;
				for (i = shape_origin.y; i < base_point.y; i++)
					lines.push_back( line );
			}
			else
			{
				shape_origin.y = base_point.y;
			}

			// create top half of mask
			half_segment_height = left_tile_base.y - base_point.y;
			for (i = 0; i < half_segment_height; i++)
			{
				width = 2 * (i + 1) * scale;
				if (cell_offset.column == 0)
					line.left = 0;
				else
					line.left = center - width;
				if (cell_offset.row == 0)
					line.right = image_width;
				else
					line.right = center + width;
				lines.push_back( line );
			}
			// create bottom half of mask
			segment_height = bottom_tile_base.y - base_point.y - half_segment_height;
			for (i = 0; i < segment_height; i++)
			{
				width = 2 * (i + 1) * scale;

				if (cell_offset.row + segment_size != m_footprint_size.row)
					line.left = left_tile_base.x + width;
				else if (cell_offset.column == 0)
					line.left = 0;
				else
					line.left = left_tile_base.x;

				if (cell_offset.column + segment_size != m_footprint_size.column)
					line.right = right_tile_base.x - width;
				else if (cell_offset.row == 0)
					line.right = image_width;
				else
					line.right = right_tile_base.x;

				lines.push_back( line );
			}
			if (cell_offset.row + segment_size == m_footprint_size.row
				|| cell_offset.column + segment_size == m_footprint_size.column)
			{
				if (cell_offset.row + segment_size != m_footprint_size.row)
					line.left = center;
				else if (cell_offset.column == 0)
					line.left = 0;
				else
					line.left = left_tile_base.x;

				if (cell_offset.column + segment_size != m_footprint_size.column)
					line.right = center;
				else if (cell_offset.row == 0)
					line.right = image_width;
				else
					line.right = right_tile_base.x;

				for (i = base_point.y + segment_height + half_segment_height; 
				     i < image_rect.bottom; i++)
					lines.push_back( line );
			}

			t_bitmap_group    segment_bitmaps;
			t_object_segment* segment;

			copy_bitmaps( bitmaps, segment_bitmaps, shape_origin, lines );
			if (segment_bitmaps.get_rect().width() == 0)
				continue;

			segment = new t_object_segment( source, segment_size,
				                            cell_offset << k_battlefield_subcell_shift,
											source, segment_bitmaps );
			base_point.y += cell_offset.height * scale;
			segment->set_offset( -base_point );
			m_segments.push_back( segment );
		}
	}
}

// --------------------------------------------------------------------------
// t_combat_object_model_base class
// --------------------------------------------------------------------------
t_compound_object_model::~t_compound_object_model()
{
}

